#ifndef __MESSAGE_H__
#define __MESSAGE_H__
#include <netinet/in.h>
#include <iostream>
#include <string>
#include <cstring>
#include <unordered_map>
#include <vector>
#include <memory>
class Message {
public:
    enum msgType { Prepare = 1, Promise, Propose, Accept };
    msgType typ;
    int from;
    int to;
    int seq;
    int preSeq;
    std::string val;

public:
    friend std::ostream &operator<<(std::ostream &output, const Message &msg) {
        static std::unordered_map<int, std::string> typeToStr = {
            {int(msgType::Prepare), "Prepare"},
            {int(msgType::Promise), "Promise"},
            {int(msgType::Propose), "Propose"},
            {int(msgType::Accept), "Accept"}};
        output << "{ typ: " << typeToStr[int(msg.typ)] << " from: " << msg.from
               << " to: " << msg.to << " "
               << " seq: " << msg.seq << " preSeq: " << msg.preSeq
               << " val: " << msg.val << " }";
        return output;
    }
    Message() = default;
    unsigned short computeCheckSum(std::vector<char> &buf) {
        unsigned int sum = 0;
        int len = buf.size();
        unsigned short *u16Buf = (unsigned short *)&buf[0];
        while (len > 1) {
            sum += *u16Buf++;
            len -= 2;
        }
        if (len > 0) {
            sum += *u16Buf & ntohs(0xff00);
        }
        sum = (sum >> 16) + (sum & 0xffff);
        sum += (sum >> 16);
        sum = ~sum;
        return (unsigned short)sum;
    }
    int msgToBuf(std::vector<char> &buf) {
        int offset = 0;
        buf.resize(9 * sizeof(int) + val.size(), 0);
        unsigned int *u32Buf = (unsigned int *)&buf[0];
        u32Buf[0] = htonl(int('m'));
        u32Buf[1] = htonl(buf.size() - sizeof(int) * 3);
        unsigned int *u32CheckSum = u32Buf + 2;
        u32Buf[2] = 0ul;
        u32Buf[3] = htonl(typ);
        u32Buf[4] = htonl(from);
        u32Buf[5] = htonl(to);
        u32Buf[6] = htonl(seq);
        u32Buf[7] = htonl(preSeq);
        u32Buf[8] = htonl(val.size());
        char *u8Buf = (char *)&u32Buf[9];
        memcpy(u8Buf, val.c_str(), val.size());
        *u32CheckSum = computeCheckSum(buf);
        return buf.size();
    }
    int bufToMsg(std::vector<char> &buf) {
        unsigned int *u32Buf = (unsigned int *)&buf[0];
        if (char(ntohl(u32Buf[0])) != 'm') {
            return -1;
        }
        int len = ntohl(u32Buf[1]);
        if (len + 3 * sizeof(int) != buf.size()) {
            return -2;
        }
        if (computeCheckSum(buf) != 0) {
            return -3;
        }
        typ = msgType(ntohl(u32Buf[3]));
        from = ntohl(u32Buf[4]);
        to = ntohl(u32Buf[5]);
        seq = ntohl(u32Buf[6]);
        preSeq = ntohl(u32Buf[7]);
        int strLen = ntohl(u32Buf[8]);
        if (strLen + sizeof(int) * 6 != len) {
            return -4;
        }
        for (int i = 9 * sizeof(int); i < buf.size(); i++) {
            val += buf[i];
        }
        return buf.size();
    }
};
#endif